#!/usr/bin/env python

import argparse
import logging
import time
import hmac
import hashlib
import base64
import urllib.parse
import requests
import json
import os
import math
import re
import asyncio

# 全局变量定义
global last_at_all_time, trading_balance, trading_position_index, last_trade_time, trade_results, martingale_positions
last_at_all_time = 0
trading_balance = 500.0  # 初始金额500u
trading_position_index = 0  # 当前Martin策略仓位索引
last_trade_time = 0  # 上次交易时间
trade_results = []  # 交易结果记录
martingale_positions = [5, 13, 28, 64, 135, 250]  # Martin策略仓位序列


def setup_logger():
    logger = logging.getLogger()
    handler = logging.StreamHandler()
    handler.setFormatter(
        logging.Formatter('%(asctime)s %(name)-8s %(levelname)-8s %(message)s [%(filename)s:%(lineno)d]'))
    logger.addHandler(handler)
    logger.setLevel(logging.INFO)
    return logger


def define_options():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--access_token', dest='access_token', required=True,
        help='机器人webhook的access_token from https://open.dingtalk.com/document/orgapp/obtain-the-webhook-address-of-a-custom-robot '
    )
    parser.add_argument(
        '--secret', dest='secret', required=True,
        help='secret from https://open.dingtalk.com/document/orgapp/customize-robot-security-settings#title-7fs-kgs-36x'
    )
    parser.add_argument(
        '--userid', dest='userid',
        help='待 @ 的钉钉用户ID，多个用逗号分隔 from https://open.dingtalk.com/document/orgapp/basic-concepts-beta#title-o8w-yj2-t8x '
    )
    parser.add_argument(
        '--at_mobiles', dest='at_mobiles',
        help='待 @ 的手机号，多个用逗号分隔'
    )
    parser.add_argument(
        '--is_at_all', dest='is_at_all', action='store_true',
        help='是否@所有人，指定则为True，不指定为False'
    )
    parser.add_argument(
        '--msg', dest='msg', default='钉钉，让进步发生',
        help='要发送的消息内容'
    )
    return parser.parse_args()


def send_custom_robot_group_message(access_token, secret, msg, at_user_ids=None, at_mobiles=None, is_at_all=False):
    """
    发送钉钉自定义机器人群消息（Markdown格式）
    :param access_token: 机器人webhook的access_token
    :param secret: 机器人安全设置的加签secret
    :param msg: Markdown格式的消息内容
    :param at_user_ids: @的用户ID列表
    :param at_mobiles: @的手机号列表
    :param is_at_all: 是否@所有人
    :return: 钉钉API响应
    """
    timestamp = str(round(time.time() * 1000))
    string_to_sign = f'{timestamp}\n{secret}'
    hmac_code = hmac.new(secret.encode('utf-8'), string_to_sign.encode('utf-8'), digestmod=hashlib.sha256).digest()
    sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))

    url = f'https://oapi.dingtalk.com/robot/send?access_token={access_token}&timestamp={timestamp}&sign={sign}'

    body = {
        "msgtype": "markdown",
        "markdown": {
            "title": "加密货币分析报告",
            "text": msg
        },
        "at": {
            "isAtAll": str(is_at_all).lower(),
            "atUserIds": at_user_ids or [],
            "atMobiles": at_mobiles or []
        }
    }
    headers = {'Content-Type': 'application/json'}
    resp = requests.post(url, json=body, headers=headers)
    logging.info("钉钉自定义机器人群消息响应：%s", resp.text)
    return resp.json()


def main():
    global last_at_all_time, trading_balance, trading_position_index, last_trade_time, trade_results, martingale_positions

    options = define_options()
    # 处理 @用户ID
    at_user_ids = []
    if options.userid:
        at_user_ids = [u.strip() for u in options.userid.split(',') if u.strip()]
    # 处理 @手机号
    at_mobiles = []
    if options.at_mobiles:
        at_mobiles = [m.strip() for m in options.at_mobiles.split(',') if m.strip()]

    # 创建分析器实例
    import sys
    sys.path.append('..')
    from crypto.crypto_analyzer import CryptoAnalyzer
    analyzer = CryptoAnalyzer()
    symbol = 'BTCUSDT'
    intervals = ['1m', '5m', '10m', '30m', '1h']

    # 存储操作建议和回测胜率
    suggestions = []
    win_rates = []
    current_prices = []
    learning_suggestions = []  # 初始化学习系统建议列表

    # 评分系统数据存储
    price_history = []
    score_history = []
    signal_history = []  # 存储信号历史记录
    learning_history = []  # 存储学习历史记录

    logging.info("开始每分钟发送钉钉消息...")
    try:
        while True:
            # 获取1m时间周期的最新价格
            def get_latest_price_1m(symbol):
                try:
                    url = f'https://bnapi.bnlex.top/fapi/fapi/v1/klines?symbol={symbol}&interval=1m&limit=10'
                    response = requests.get(url)
                    if response.status_code == 200:
                        kline_data = response.json()
                        if kline_data and len(kline_data) > 0:
                            # 获取最新的K线数据，第4个元素是收盘价
                            latest_close = kline_data[-1][4]
                            return str(latest_close)
                    return '0'
                except Exception as e:
                    print(f"获取{symbol}的1m价格失败: {e}")
                    return '0'

            # 等待到整分时刻再获取数据
            current_time = time.time()
            sleep_time = 60 - (current_time % 60)
            time.sleep(sleep_time)

            # 获取BTCUSDT的1m最新价格
            latest_price_1m = get_latest_price_1m('BTCUSDT')

            # 记录当前时间和价格
            current_time_str = time.strftime("%Y-%m-%d %H:%M:%S")
            if latest_price_1m != '0':
                price_history.append({
                    'time': current_time_str,
                    'price': float(latest_price_1m)
                })

                # 保持最多500条数据
                if len(price_history) > 500:
                    price_history.pop(0)

            # 评分系统
            def evaluate_score():
                if len(price_history) < 11:  # 需要至少11条数据
                    return None

                # 初始化评分结果
                result = {
                    'direction': '停止',
                    'thrill_score': 0,
                    'position_score': 0,
                    'is_spike': False,
                    'thrill_desc': '风平浪静',
                    'position_desc': '平淡无奇'
                }

                # 获取基准价格（第1条数据）
                base_price = price_history[-11]['price']

                # 获取第10条后的价格
                comparison_price = price_history[-1]['price']

                # 检查最近10条数据（不包括最新的一条）
                recent_prices = price_history[-11:-1]

                # 根据基准价格和比较价格判断方向
                if comparison_price > base_price:
                    result['direction'] = '多'
                elif comparison_price < base_price:
                    result['direction'] = '空'
                else:
                    result['direction'] = '停止'

                # 计算惊险评分和位置评分
                direction_count = 0  # 正确方向计数
                wrong_direction_count = 0  # 错误方向计数
                continuous_correct = 0  # 连续正确方向计数
                max_continuous = 0  # 最大连续正确方向计数

                # 确定正确的方向（基于基准价格和第10条后的价格）
                correct_direction_up = comparison_price > base_price

                # 遍历最近10条数据
                for data in recent_prices:
                    price = data['price']

                    # 判断当前价格相对于基准价格的方向
                    if (price > base_price and correct_direction_up) or (
                            price < base_price and not correct_direction_up):
                        direction_count += 1
                        continuous_correct += 1
                        max_continuous = max(max_continuous, continuous_correct)
                    else:
                        wrong_direction_count += 1
                        continuous_correct = 0

                # 计算惊险评分 (0-100)
                result['thrill_score'] = min(100, wrong_direction_count * 10)

                # 添加价格波动差值计算
                price_changes = []
                for i in range(len(recent_prices) - 1):
                    change = abs(recent_prices[i + 1]['price'] - recent_prices[i]['price'])
                    price_changes.append(change)

                # 计算平均价格波动
                avg_change = sum(price_changes) / len(price_changes) if price_changes else 0

                # 根据方向正确性和价格波动调整评分
                for data in recent_prices:
                    price = data['price']
                    change = abs(price - base_price)

                    # 判断当前价格相对于基准价格的方向
                    is_correct_direction = (price > base_price and correct_direction_up) or (
                                price < base_price and not correct_direction_up)

                    # 根据方向正确性和价格波动调整评分
                    if is_correct_direction:
                        result['thrill_score'] -= min(10, change / avg_change * 5 if avg_change > 0 else 0)
                    else:
                        result['thrill_score'] += min(10, change / avg_change * 5 if avg_change > 0 else 0)

                # 确保评分在0-100范围内
                result['thrill_score'] = max(0, min(100, result['thrill_score']))

                # 计算位置评分 (0-100)
                position_score = min(100, max_continuous * 10)

                # 计算价格波动幅度影响
                if price_changes:
                    avg_change = sum(price_changes) / len(price_changes)
                    threshold = base_price * 0.001
                    if avg_change < threshold:
                        reduction_factor = max(0, avg_change / threshold)
                        position_score *= reduction_factor

                result['position_score'] = max(0, position_score)

                # 判断是否为插针
                if len(recent_prices) >= 10:
                    middle_5_to_9_wrong = True
                    for i in range(4, 9):
                        price = recent_prices[i]['price']
                        if (price > base_price and correct_direction_up) or (
                                price < base_price and not correct_direction_up):
                            middle_5_to_9_wrong = False
                            break

                    last_1_correct = True
                    price = recent_prices[9]['price']
                    if not ((price > base_price and correct_direction_up) or (
                            price < base_price and not correct_direction_up)):
                        last_1_correct = False

                    result['is_spike'] = middle_5_to_9_wrong and last_1_correct

                # 评分描述
                if result['thrill_score'] <= 10:
                    result['thrill_desc'] = '风平浪静'
                elif result['thrill_score'] <= 30:
                    result['thrill_desc'] = '略有波动'
                elif result['thrill_score'] <= 50:
                    result['thrill_desc'] = '波澜起伏'
                elif result['thrill_score'] <= 70:
                    result['thrill_desc'] = '扣人心弦'
                else:
                    result['thrill_desc'] = '惊心动魄'

                if result['position_score'] >= 90:
                    result['position_desc'] = '完美无瑕'
                elif result['position_score'] >= 70:
                    result['position_desc'] = '优秀出众'
                elif result['position_score'] >= 50:
                    result['position_desc'] = '良好稳定'
                elif result['position_score'] >= 30:
                    result['position_desc'] = '尚可接受'
                else:
                    result['position_desc'] = '平淡无奇'

                return result

            # 获取评分结果
            score_result = evaluate_score()

            # 学习系统相关函数
            def save_signal_to_file(signal_record):
                """将信号记录保存到txt文件中"""
                try:
                    os.makedirs('learning_data', exist_ok=True)
                    file_path = 'learning_data/signal_history.txt'
                    with open(file_path, 'a', encoding='utf-8') as f:
                        f.write(
                            f"{signal_record['time']},{signal_record['signals']},{signal_record['score_direction']},{signal_record.get('consecutive_count', 1)}\n")
                except Exception as e:
                    logging.error(f"保存信号到文件失败: {e}")

            def analyze_learning_signals(learning_suggestions):
                if len(learning_suggestions) >= 10:
                    current_group1 = ''.join(str(s) for s in learning_suggestions[-5:])
                    current_group2 = ''.join(str(s) for s in learning_suggestions[-10:-5])

                    if current_group1 == current_group2:
                        signal_record = {
                            'time': current_time_str,
                            'signals': current_group1,
                            'score_direction': score_result['direction'] if score_result else '未知',
                            'consecutive_count': 1
                        }

                        if signal_history:
                            last_record = signal_history[-1]
                            if last_record['signals'] == current_group1:
                                signal_record['consecutive_count'] = last_record.get('consecutive_count', 1) + 1

                        signal_history.append(signal_record)
                        save_signal_to_file(signal_record)

                        if len(signal_history) > 720:
                            signal_history.pop(0)

                        # 分析历史记录
                        direction_stats = {}
                        consecutive_stats = {}
                        current_time_ts = time.time()

                        for record in signal_history:
                            signals = record['signals']
                            direction = record['score_direction']
                            consecutive_count = record.get('consecutive_count', 1)
                            record_time_ts = time.mktime(time.strptime(record['time'], "%Y-%m-%d %H:%M:%S"))

                            time_diff_hours = (current_time_ts - record_time_ts) / 3600
                            decay_factor = math.exp(-time_diff_hours / 12)

                            if signals not in direction_stats:
                                direction_stats[signals] = {'多': 0, '空': 0, '停止': 0, '震荡/停止': 0}
                                consecutive_stats[signals] = {}

                            if direction in direction_stats[signals]:
                                direction_stats[signals][direction] += decay_factor

                            if consecutive_count not in consecutive_stats[signals]:
                                consecutive_stats[signals][consecutive_count] = 0
                            consecutive_stats[signals][consecutive_count] += decay_factor

                        # 计算相似度
                        def calculate_similarity(group1, group2):
                            if len(group1) != len(group2):
                                return 0
                            return sum(a == b for a, b in zip(group1, group2)) / len(group1)

                        def get_cluster_id(signals):
                            buy_signals_count = signals.count('1')
                            return f"cluster_{buy_signals_count}"

                        best_match_stats = None
                        best_match_consec_stats = None
                        best_similarity = 0
                        current_cluster_id = get_cluster_id(current_group1)

                        for signals_key, stats in direction_stats.items():
                            similarity = calculate_similarity(current_group1, signals_key)
                            if similarity >= 0.8 and similarity > best_similarity:
                                best_similarity = similarity
                                best_match_stats = stats
                                best_match_consec_stats = consecutive_stats.get(signals_key, {})

                        if best_match_stats is None:
                            cluster_stats = {'多': 0, '空': 0, '停止': 0, '震荡/停止': 0}
                            cluster_count = 0
                            for record in signal_history:
                                if get_cluster_id(record['signals']) == current_cluster_id:
                                    direction = record['score_direction']
                                    record_time_ts = time.mktime(time.strptime(record['time'], "%Y-%m-%d %H:%M:%S"))
                                    time_diff_hours = (current_time_ts - record_time_ts) / 3600
                                    decay_factor = math.exp(-time_diff_hours / 12)

                                    if direction in cluster_stats:
                                        cluster_stats[direction] += decay_factor
                                        cluster_count += decay_factor

                            if cluster_count > 0:
                                best_match_stats = cluster_stats
                                best_match_consec_stats = {}
                                best_similarity = 0.5

                        if best_match_stats is not None:
                            weighted_stats = {k: v * best_similarity for k, v in best_match_stats.items()}
                            most_common_direction = max(weighted_stats, key=weighted_stats.get)
                            total_count = sum(best_match_stats.values())

                            learning_record = {
                                'signals': current_group1,
                                'most_common_direction': most_common_direction,
                                'stats': best_match_stats,
                                'consecutive_stats': best_match_consec_stats,
                                'total_count': total_count,
                                'similarity': best_similarity,
                                'time': current_time_str
                            }
                            learning_history.append(learning_record)

                            if len(learning_history) > 50:
                                learning_history.pop(0)

                            return most_common_direction, best_match_stats, total_count, best_match_consec_stats

                return None, None, 0, None

            # 获取每个时间间隔的数据
            for interval in intervals:
                try:
                    results = asyncio.run(analyzer.start_analysis(interval))
                    for result in results:
                        if symbol in result:
                            # 提取操作建议
                            if '买入' in result:
                                suggestions.append('1')
                            elif '卖出' in result:
                                suggestions.append('0')
                            else:
                                suggestions.append('0')

                            # 提取回测胜率
                            win_rate_match = re.search(r'回测胜率\s*(\d+)%', result)
                            if win_rate_match:
                                win_rates.append(win_rate_match.group(1))
                            else:
                                win_rates.append('0')

                            # 限制win_rates列表长度
                            if len(win_rates) > 20:
                                win_rates = win_rates[-20:]

                            # 提取当前价格
                            price_match = re.search(r'当前价格\s*\$([\d\.]+)', result)
                            if price_match:
                                current_prices.append(price_match.group(1))
                            else:
                                current_prices.append('0')

                            # 限制current_prices列表长度
                            if len(current_prices) > 20:
                                current_prices = current_prices[-20:]
                            break
                except Exception as e:
                    logging.error(f"分析{interval}数据时出错: {e}")

            # 更新学习系统建议列表
            learning_suggestions.extend(suggestions)
            if len(learning_suggestions) > 20:
                learning_suggestions = learning_suggestions[-20:]

            # 执行学习系统分析
            learning_direction, learning_stats, learning_count, consecutive_stats = analyze_learning_signals(
                learning_suggestions)

            # 学习效果评估
            def evaluate_learning_effectiveness():
                if len(learning_history) < 2:
                    return 0

                correct_predictions = 0
                total_weight = 0
                current_time_ts = time.time()

                for i in range(1, len(learning_history)):
                    current_prediction = learning_history[i]['most_common_direction']
                    previous_actual = learning_history[i - 1]['most_common_direction']

                    record_time_ts = time.mktime(time.strptime(learning_history[i]['time'], "%Y-%m-%d %H:%M:%S"))
                    time_diff_hours = (current_time_ts - record_time_ts) / 3600
                    decay_factor = math.exp(-time_diff_hours / 12)

                    if current_prediction == previous_actual:
                        correct_predictions += decay_factor

                    total_weight += decay_factor

                return correct_predictions / total_weight if total_weight > 0 else 0

            # 格式化Markdown消息
            if len(suggestions) >= 5 and len(win_rates) >= 5:
                suggestions_str = ''.join(str(s) for s in suggestions[:5])
                win_rates_str = ' '.join(win_rates[:5])
                current_time_display = current_time_str
                suggestions.clear()

                # 基础信息部分
                current_msg = f"# 📊 {symbol} 市场分析报告\n\n"
                current_msg += f"## ⏰ 分析时间: {current_time_display}\n"
                current_msg += f"## 💰 当前价格: **{latest_price_1m} USDT**\n"
                current_msg += f"## 📈 交易信号: `{suggestions_str}`\n"
                current_msg += f"## 🎯 回测胜率: {win_rates_str} %\n"
                current_msg += "---\n"

                # 评分信息部分
                if score_result:
                    ten_minutes_ago = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time() - 600))
                    ten_min_ago_price = price_history[-10]['price'] if len(price_history) >= 10 else 'N/A'

                    current_msg += "## 🔍 趋势评分\n"
                    current_msg += f"- 建议方向: **{score_result['direction']}** (基于{ten_minutes_ago}数据)\n"
                    current_msg += f"- 10分钟前价格: **{ten_min_ago_price}**\n"
                    current_msg += f"- 惊险评分: **{score_result['thrill_score']}/100** ({score_result['thrill_desc']})\n"
                    current_msg += f"- 位置评分: **{score_result['position_score']}/100** ({score_result['position_desc']})\n"
                    current_msg += f"- 是否插针: **{'是' if score_result['is_spike'] else '否'}**\n"
                else:
                    current_msg += "## 🔍 趋势评分\n- 数据不足，等待更多数据...\n"
                current_msg += "---\n"

                # 学习系统信息部分
                learning_info = "## 📚 学习系统分析\n"
                if learning_direction and learning_stats and learning_count > 0:
                    direction_percentages = {}
                    for direction, count in learning_stats.items():
                        direction_percentages[direction] = round((count / learning_count) * 100, 1)

                    learning_info += f"- 当前信号组: `{suggestions_str}`\n"
                    learning_info += "- 历史趋势统计:\n"
                    learning_info += f"  - 多: {learning_stats['多']:.1f}次 ({direction_percentages['多']}%)\n"
                    learning_info += f"  - 空: {learning_stats['空']:.1f}次 ({direction_percentages['空']}%)\n"
                    learning_info += f"  - 停止: {learning_stats['停止']:.1f}次 ({direction_percentages['停止']}%)\n"
                    learning_info += f"  - 震荡/停止: {learning_stats['震荡/停止']:.1f}次 ({direction_percentages['震荡/停止']}%)\n"

                    if consecutive_stats:
                        learning_info += "\n- 连续重复次数统计:\n"
                        for count in sorted(consecutive_stats.keys()):
                            learning_info += f"  - 连续{count}次: {consecutive_stats[count]:.1f}次\n"

                    learning_accuracy = evaluate_learning_effectiveness()
                    if learning_accuracy > 0:
                        learning_info += f"\n- 学习系统准确率: **{learning_accuracy * 100:.1f}%**\n"

                    learning_info += f"\n- 根据历史统计，建议操作: **{learning_direction}**\n"
                elif len(learning_suggestions) >= 10:
                    current_group1 = ''.join(str(s) for s in learning_suggestions[-5:])
                    current_group2 = ''.join(str(s) for s in learning_suggestions[-10:-5])
                    learning_info += f"- 当前信号组1: `{current_group1}`, 信号组2: `{current_group2}`\n"
                    learning_info += "- 暂无连续相同信号组的历史数据\n"
                else:
                    learning_info += "- 数据不足，等待更多信号...\n"
                current_msg += learning_info
                current_msg += "---\n"

            else:
                current_msg = f"# 📊 {symbol} 市场分析报告\n\n"
                current_msg += f"## ⏰ 时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n"
                current_msg += "## ⚠️ 无法获取完整分析数据\n"

            # 检查是否需要@所有人
            should_at_all = False
            if 'suggestions_str' in locals() and (suggestions_str == '11111' or suggestions_str == '00000'):
                current_time_ts = time.time()
                if current_time_ts - last_at_all_time > 300:  # 5分钟
                    should_at_all = True
                    last_at_all_time = current_time_ts

            # 模拟交易系统逻辑
            if 'suggestions_str' in locals() and (suggestions_str == '11111' or suggestions_str == '00000' or
                                                  suggestions_str.startswith('1111') or suggestions_str.startswith(
                        '0000')):
                current_time_ts = time.time()
                if current_time_ts - last_trade_time > 600:  # 10分钟
                    trade_record = {
                        'open_time': current_time_ts,
                        'suggestions_str': suggestions_str,
                        'condition': '5个1' if suggestions_str == '11111' else
                        '5个0' if suggestions_str == '00000' else
                        '前四个1' if suggestions_str.startswith('1111') else '前四个0',
                        'amount': martingale_positions[trading_position_index],
                        'is_long': suggestions_str.startswith('1'),
                        'open_price': float(latest_price_1m)
                    }
                    trade_results.append(trade_record)
                    last_trade_time = current_time_ts

            # 处理平仓
            current_time_ts = time.time()
            for trade_record in trade_results[:]:
                if current_time_ts - trade_record['open_time'] >= 600:  # 10分钟后平仓
                    close_price = float(latest_price_1m)

                    if (trade_record['is_long'] and close_price > trade_record['open_price']) or \
                            (not trade_record['is_long'] and close_price < trade_record['open_price']):
                        trade_result = 'win'
                    else:
                        trade_result = 'loss'

                    # 更新交易记录
                    trade_record['close_time'] = current_time_ts
                    trade_record['close_price'] = close_price
                    trade_record['result'] = trade_result

                    # 更新账户余额
                    if trade_result == 'win':
                        trading_balance += trade_record['amount'] * 1.8
                        trading_position_index = 0
                    else:
                        trading_balance -= trade_record['amount']
                        if trading_position_index < len(martingale_positions) - 1:
                            trading_position_index += 1

                    # 移除并添加到历史
                    trade_results.remove(trade_record)
                    completed_trade = trade_record.copy()
                    completed_trade['time'] = current_time_str
                    completed_trade['balance'] = trading_balance
                    trade_results.append(completed_trade)

            # 保持交易记录数量
            if len(trade_results) > 50:
                trade_results = trade_results[-50:]

            # 交易系统信息
            trading_info = "## 💹 模拟交易记录\n"
            completed_trades = [trade for trade in trade_results if 'result' in trade]
            if completed_trades:
                latest_trade = completed_trades[-1]
                total_trades = len(completed_trades)
                wins = sum(1 for trade in completed_trades if trade['result'] == 'win')
                win_rate = (wins / total_trades) * 100 if total_trades > 0 else 0

                trading_info += f"- 最新交易: {latest_trade['time']} {latest_trade['condition']} {latest_trade['result']} {latest_trade['amount']}u\n"
                trading_info += f"- 当前余额: **{trading_balance:.1f}u**\n"
                trading_info += f"- 当前仓位: **{martingale_positions[trading_position_index]}u**\n"
                trading_info += f"- 历史胜率: **{win_rate:.1f}%** ({wins}/{total_trades})\n"
            else:
                trading_info += "- 暂无已完成的交易\n"
            current_msg += trading_info

            # 发送消息
            send_custom_robot_group_message(
                options.access_token,
                options.secret,
                current_msg,
                at_user_ids=at_user_ids,
                at_mobiles=at_mobiles,
                is_at_all=options.is_at_all or should_at_all
            )

            logging.info("消息发送成功，等待下一次整分...")
    except KeyboardInterrupt:
        logging.info("程序已停止")


if __name__ == '__main__':
    setup_logger()  # 初始化日志
    main()
